# In this example we will see how a model and views communicate with each other in Qt and also, how a model stores a data.
# So we will create our own model class and understand how a model interacts with a view.
# There are three ways in which the data can be represented 1) List based (1-Dim), 2) Table based (2-Dim) and 3) Tree based (Hierarchical).
# In this example we will work with List based model.
# A model is just an interface that the view communicates with to retrieve some data to display. The model is just a wrapper for the data.
# The view doesn't know how to get the data. All it can do is pass some values to the model and hope for the best. Its the model's job to
# take the coming values from the view and translate them. The model will store QColor objects

import sys
from PySide import QtGui, QtCore


# To create a model, we have to subclass one of the abstract models. We will be subclassing QAbstractList model. All the models starting
# with abstract have to be subclasses. You can't use them directly.
class PalletteListModel(QtCore.QAbstractListModel):
    # The model will receive a list of colors and the parent.
    def __init__(self, colors = [], parent = None):
        # First we need to call the super-class constructor and pass the parent argument to it
        QtCore.QAbstractListModel.__init__(self, parent)
        # Since we are making a model that will store colors, we need a private list that would take the incoming color list.
        self.__colors = colors
        
    # Every model in Qt must implement two methods at minimum, i.e. 1) row count and 2) data
    # The row count tell the view how many item this model contains. For a list based model, we can ignore the parent but for hirarchical based
    # view, the parent will be important.
    def rowCount(self, parent):
        return len(self.__colors)
    
    # The data method is responsible to provide the data to be displayed by the view based on its index value
    def data(self, index, role):

        # For displaying tooltip
        if role == QtCore.Qt.ToolTipRole:
            return "Hex code is: " + self.__colors[index.row()].name()
        
        # For displaying the color icon
        if role == QtCore.Qt.DecorationRole:
            row = index.row()
            value = self.__colors[row]
            # First create a pixmap
            pixmap = QtGui.QPixmap(30, 30)
            pixmap.fill(value)
            # Then create an icon with that pixmap
            icon = QtGui.QIcon(pixmap)
            return icon
            
        # For displaying value
        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            value = self.__colors[row]
            return value.name()
        
        # For keeping the old data there when double clicked to edit
        if role == QtCore.Qt.EditRole:
            return self.__colors[index.row()].name()
    
    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            
            if orientation == QtCore.Qt.Horizontal:
                return "Pallette"
            else:
                return "Color %s" % section
    
    # To make the items editable
    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
    
    def setData(self, index, value, role = QtCore.Qt.EditRole):
        # Index -> stores information where the item is.
        # value -> The edited value (it is always a string). We take that string, convert it to a caller?? and assign it to our
        # private list in the edited row. If the operation is successful, returns true.
        if role == QtCore.Qt.EditRole: # It is also the default role as we set it already
            row = index.row()
            color = QtGui.QColor(value)
            if color.isValid():
                self.__colors[row] =  color
                self.dataChanged.emit(index, index)
                return True # if color is valid then return true else return false
            return False
        
    # Inserting data into the model
    def insertRows(self, position, rows, parent = QtCore.QModelIndex()):
        # position -> where in the data structure we want to insert
        # rows -> how many rows we want to insert
        # parent -> The parent is defaulted to an empty QModelIndex
        self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
        # self.beginInsertRows(index, first, last)
        # First and Last are the indices of where the views should update. All items between the first
        # and last including those two are the items we are inserting and the views must know those indices so they can display the correct data.
        # The index parameter is only used when we are working with hierarchical data structures. Here we want to default to root. 
        for i in range(rows):
            self.__colors.insert(position, QtGui.QColor('#000000'))
        self.endInsertRows()
        return True
    
    # Removing data from the model
    def removeRows(self, position, rows, parent = QtCore.QModelIndex()):
        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
        for i in range(rows):
            value = self.__colors[position]
            self.__colors.remove(value)
            
        self.endRemoveRows()
        return True


if __name__ == '__main__':
    appObject = QtGui.QApplication(sys.argv)
    appObject.setStyle("plastique")
    
    # All of our views
    listview = QtGui.QListView()
    listview.show()
    
    treeview = QtGui.QTreeView()
    treeview.show()
    
    combobox = QtGui.QComboBox()
    combobox.show()
    
    tableview = QtGui.QTableView()
    tableview.show()
    
    red = QtGui.QColor(255, 0, 0)
    green = QtGui.QColor(0, 255, 0)
    blue = QtGui.QColor(0, 0, 255)
    
    # Create the model from the PalletteListModel
    model = PalletteListModel([red, green, blue])
    
    # Assign the model to views
    listview.setModel(model)
    treeview.setModel(model)
    combobox.setModel(model)
    tableview.setModel(model)
    
    # Insert some rows at index 2
    model.insertRows(2, 5)
    
    # Remove some rows at index 2
    model.removeRows(2, 5)
    
    sys.exit(appObject.exec_())
    